local jass = require 'jass.common'
local japi = require 'jass.japi'
local dbg  = require 'jass.debug'

local handle_manager = handle_manager_class:create()

--[特效]
---@class effect "特效"
local effect = {}
setmetatable(effect, effect)
ac.effect = effect

--[特效显示]
effect.state = 1

--[存放所有特效]
effect.alleffects = {}

function effect.disable()
    effect.state = 0
end 

function effect.enable()
    effect.state = 1
end 

local mt = {}
effect.__index = mt 

--[结构]
mt.type = 'effect'

--[句柄]
mt.handle = jass.ConvertUnitState(0)

--[模型路径]
mt.model = '.mdx' 

--[部位] (字符串 - 绑在目标身上的哪个位置)
mt.socket = 'origin'

--[颜色]
mt.color = 0xffffffff

--[重置特效]
function mt:matreset()
    self._rotate_z = 0
    self._rotate_x = 0
    self._rotate_y = 0
    japi.EXEffectMatReset(self.handle)
end

--[获取旋转值]
function mt:get_rotate()
    return self._rotate_x, self._rotate_y, self._rotate_z
end

--[按坐标X轴旋转]
function mt:rotate_x(r)
    self._rotate_x = self._rotate_x + r
    japi.EXEffectMatRotateX(self.handle, r)
end

--[按坐标Y轴旋转]
function mt:rotate_y(r)
    self._rotate_y = self._rotate_y + r
    japi.EXEffectMatRotateY(self.handle, r)
end

--[按坐标Z轴旋转]
function mt:rotate_z(r)
    self._rotate_z = self._rotate_z + r
    japi.EXEffectMatRotateZ(self.handle, r)
end

--[整体旋转]
function mt:set_rotate(x, y, z, reset)
    if reset then 
        self:matreset()
    end
    self:rotate_x(x or 0)
    self:rotate_y(y or 0)
    self:rotate_z(z or 0)
end

--[获取颜色]
function mt:get_color()
    return self.color
end 

local function to_argb(a, r, g, b)
    return math.floor(0.5 + a * 2.55) * 0x1000000 
    + math.floor(0.5 + r * 2.55) * 0x10000 
    + math.floor(0.5 + g * 2.55) * 0x100 
    + math.floor(0.5 + b * 2.55)
end

--[设置颜色] 
mt.red = 100
mt.green = 100
mt.blue = 100
mt.alpha = 100

-- 设置单位颜色
--	[红(%)]
--	[绿(%)]
--	[蓝(%)]
function mt:set_color(red, green, blue, alpha)
    self.red, self.green, self.blue = red, green, blue
    self.alpha = alpha or self.alpha
    japi.EXSetEffectColor(self.handle, to_argb(self.alpha, self.red, self.green, self.blue))
end

-- 设置单位透明度
--	透明度(%)
function mt:set_alpha(alpha)
    self.alpha = alpha
    japi.EXSetEffectColor(self.handle, to_argb(self.alpha, self.red, self.green, self.blue))
end

function mt:get_alpha() 
    return self.alpha 
end

--[设置朝向]
function mt:set_facing(angle)
    japi.EXEffectMatReset(self.handle)
    japi.EXEffectMatRotateZ(self.handle, angle)
    return self
end 

--[设置动画速度]
function mt:set_speed(speed)
    japi.EXSetEffectSpeed(self.handle, speed)
    return self
end

--[设置模型]
function mt:set_model(model)
    self.model = model 
    if not self._target then 
        if effect.state == 1 then 
            japi.SetUnitModel(self.handle, model)
        end 
    end 
    return self
end 

--[按ID设置单位的贴图]
function mt:set_texture(normal_image, id)
    japi.SetUnitTexture(self.handle, normal_image or "", id or 32)
    return self
end

--[设置队伍颜色]
function mt:set_team_color(id)
    japi.SetUnitTexture(self.handle, string.format("ReplaceableTextures\\TeamColor\\TeamColor%02d.blp", id), 1)
    japi.SetUnitTexture(self.handle, string.format("ReplaceableTextures\\TeamGlow\\TeamGlow%02d.blp", id), 2)
end

--[设置动画]
function mt:set_animation(aniamtion)
    japi.EXSetEffectAnimation(self.handle, aniamtion or "stand")
end

--[播放动画]
function mt:play_animation(aniamtion,atlname)
    japi.EXPlayEffectAnimation(self.handle, aniamtion or "stand", atlname or "")
end

--[设置可见]
function mt:set_visible(flag)
    japi.EXSetEffectVisible(self.handle, flag)
end

--[设置迷雾可见]
function mt:set_mask_visible(flag)
    japi.EXSetEffectMaskVisible(self.handle, flag)
end

--[设置阴影可见]
function mt:set_fog_visible(flag)
    japi.EXSetEffectFogVisible(self.handle, flag)
end

--[获取缩放]
function mt:get_scale()
    return japi.EXGetEffectSize(self.handle)
end

--[获取碰撞]
function mt:get_collision()
    return 32 * self:get_scale()
end 

--[获取弹道命中z偏移]
function mt:get_impactZ()
    return 32
end 

--[获取弹道命中点]
function mt:get_impact_point()
    local point = self:get_point()
    point.z = self:get_impactZ() + self:get_height()
    return point
end 

-- +EIP
-- +EIm
-- +EIf

--[获取高度]
function mt:get_height()
    if self._target then 
        return self._target:get_height()
    end 
    return japi.EXGetEffectZ(self.handle)
end 

--[设置高度]
function mt:set_height(height)
    if self._target then 
        return self
    end 
    japi.EXSetEffectZ(self.handle,height or 0)
    return self
end

--[获取位置]
function mt:get_point()
    if self._target then 
        return self._target:get_point()
    end 
    local x = japi.EXGetEffectX(self.handle)
    local y = japi.EXGetEffectY(self.handle)
    return ac.point(x, y)
end 

--[设置位置]
function mt:set_point(point)
    if self._target then 
        return self
    end 
    japi.EXSetEffectXY(self.handle, point:get())
    return self
end

function mt:set_position(...)
    self:set_point(...)
end

--[设置缩放]
function mt:set_scale(scale, flag)
    if self._target then 
        return 
    end 
    japi.EXSetEffectSize(self.handle, scale)
    if flag then
        pcall(japi.SetPariticle2Size, self.handle, scale)
    end 
end
--[根据x,y,z轴缩放]
function mt:set_matscale(x, y, z, reset)
    if self._target then 
        return 
    end 
    if reset then 
        self:matreset()
    end
    japi.EXEffectMatScale(self.handle, x, y, z)
end 


--[设置粒子缩放]
function mt:set_paritcle2size(scale)
    if self._target then 
        return self
    end 
    japi.SetPariticle2Size(self.handle, scale)
    return self
end 

--[摧毁]
function mt:destroy(rb)
    if self._target and self._effect_type == 1 and rb then 
        jass.DestroyEffect(jass.AddSpecialEffectTarget(self.model, self._target.handle, self.socket))
    end 
    self:remove()
end 

--[删除]
function mt:remove()
    if self.removed then return end 
    self.removed = true 
    if self._target and self._target.effects then 
        if self._effect_type == 1 then 
            self:unbind()
        else 
            self._target.effects[self._index] = nil 
        end 
    end 
    self:remove_effects()
    effect.alleffects[self._index] = nil
    handle_manager:free(self._index)
    dbg.handle_unref(self.handle)
    jass.DestroyEffect(self.handle)
    if self._lifespan_timer then
        self._lifespan_timer:remove()
    end
    if self.hide_when_remove then 
        self:set_visible(false)
    end 
    self._index = nil
    self.handle = nil
end 

--[定时删除]
function mt:lifespan(time)
    time = time or 0 
    if self._lifespan_timer then 
        self._lifespan_timer:remove()
    end 
    self._lifespan_timer = ac.wait(time * 1000, function ()
        self:remove() 
    end)
    return self 
end 

function mt:model_init()
    if self.scale then 
        self:set_scale(self.scale,true) 
    end
    -- size
    if self.size and self.size ~= 1 then
        self:set_scale(self.size,true)
    else
        self.size = 1
    end
    if self.scale_x or self.scale_y or self.scale_z then
        self:EXEffectMatScale(self.scale_x or self.size or 1, 
            self.scale_y or self.size or 1,
            self.scale_z or self.size or 1)
    end
    -- height
    if self.height ~= 0 then 
        self:set_height(self.height) 
    end
    if self.rotate then
        self:set_rotate(self.rotate[1], self.rotate[2], self.rotate[3])
    else
        self.rotate = {0, 0, 0}
    end
    if self.angle then 
        self:set_facing(self.angle) 
    end
    -- speed
    if self.speed and self.speed ~= 0 then
        self:set_speed(self.speed)
    else
        self.speed = 1
    end
    -- color
    if self.color and type(self.color) == 'table' then
        self:set_color(self.color[1], self.color[2], self.color[3], self.color[4])
    end
    if self.alpha then
        self:set_alpha(self.alpha)
    end
    if self.time then
        self:lifespan(self.time)
    end
end


-- 输出
function effect:__tostring()
    return ("特效[%d]"):format(self.handle)
end 

--[创建特效]
function effect.create(data)

    if not data.target then 
        log.error('没有指定特效目标')
        return 
    end 

    setmetatable(data, effect)
    
    data._rotate_z = 0
    data._rotate_x = 0
    data._rotate_y = 0

    data.effects = {}

    if data.target.type == 'point' then 
        data.handle = jass.AddSpecialEffect(data.model, data.target:get())
        data._effect_type = 1
    else 
        data.handle = jass.AddSpecialEffectTarget(data.model, data.target.handle, data.socket) 
        data._effect_type = 2
        data._target = data.target 
    end

    data._index = handle_manager:allocate()

    local widget = data._target

    if widget then 
        if not widget.effects then 
            widget.effects = {}
        end
        widget.effects[data._index] = data
    end 
    data:model_init()
    
    data.gchash = data.handle
    dbg.handle_ref(data.handle)
    dbg.gchash(data, data.handle)

    effect.alleffects[data._index] = data

    return data
end 

--[创建特效] 
function effect:__call(...)
    local n = select('#',...)
    if n == 1 then 
        return effect.create(...)
    end 
    local model, target, socket = ... 
    return effect.create{
        model = model,
        target = target,
        socket = socket,
    }
end 

--[绑定物体]
function mt:bind(widget, socket)
    if self._target then 
        return 
    end 
    self._target = widget
    self.socket = socket
    if not widget.effects then 
        widget.effects = {}
    end
    japi.BindEffect(widget.handle, self.socket, self.handle)
    widget.effects[self._index] = self
end 

--[解除绑定]
function mt:unbind()
    if not self._target then 
        return 
    end 
    japi.UnBindEffect(self.handle)
    self._target.effects[self._index] = nil
    self._target = nil 
end

function mt:search_effects()
    if not self.effects then 
        return function()
        end 
    end 
    local tbl = {}
    for _, effect in pairs(self.effects) do 
        table.insert(tbl, effect)
    end 
    table.sort(tbl, function(a, b)
        return a._index > b._index
    end)
    local n = 0
	return function (t, v)
		n = n + 1
		return t[n]
	end, tbl
end 

--[删除所有特效]
function mt:remove_effects()
    for eff in self:search_effects() do 
        eff:remove()
    end 
end 

--[特效绑特效]
function mt:add_attach(model, socket)
    if self._target then 
        return 
    end 
    if effect.state ~= 1 then 
        model = ""
    end 
    local eff = effect.create{
        model = model,
        target = ac.point(9999,9999,9999),
    }
    eff:bind(self, socket or 'origin')
    return eff 
end

--[创建特效]
function ac.point.__index:effect(data)
    local height = self:get_height()
    data.target = self
    data.height = (data.height or 0) + height
    return effect.create(data)
end

function ac.point.__index:add_effect(model,time)
    if effect.state ~= 1 then 
        model = ""
    end 
    local new = effect.create{
        target = self,
        model = model,
        height = self:get_height(),
        time = time,
    }
    return new 
end 

--[[
创建一个从一个点往外扩散特效
params(点,范围,速度,特效,半径间隔,每圈间隔)
]]
function ac.point.__index:create_effect_diffuse(area,speed,model,spacing,density)
    local spacing = spacing or 150
    local density = density or 70
    local count = math.max(math.floor(area/spacing))
    local time = area/speed/count
    local real_area = 0
    ac.timer(time*1000,count,function()
        real_area = real_area + spacing
        local range = 2 * math.pi * real_area
        local num = math.floor(range/density)
        for a=1,num do
            local r = a/num * 360
            local p = self - {r,real_area}
            p:effect{
                model = model,
                angle = angle,
                time = 0.1,
            }
        end
    end)
end


return effect